// File [test/pointers/checking_ptr/01/main.cpp]

#include <iostream>
#include <ostream>
#include <alfs/pointers/CheckingPtr.hpp>

class BaseClass: public alfs::destructionEvent::Source {};

class Derived: public BaseClass {};

int testAutoDestroy_nDestroyCalls = 0;
namespace std{
    template<>
    struct default_delete<Derived>
    {
        void operator()( Derived* p ) const
        {
            std::cout << "Destroyed (standard)" << std::endl;
            delete p;
            ++testAutoDestroy_nDestroyCalls;
        }
    };
}  // std

void testAutoDestroy()
{
    using namespace alfs;
    std::cout << "\n* testAutoDestroy:" << std::endl;
    {
        CheckingPtr<Derived>    p( new Derived );
        CheckingPtr<BaseClass>       q = p;
#ifdef  SHOULD_FAIL_DERIVED_FROM_BASE
        CheckingPtr<Derived>    r = q;      // Should not compile.
#endif
    }
    (testAutoDestroy_nDestroyCalls == 1) || throwX( "Failed, destroy count != 1" );
    std::cout << "Passed testAutoDestroy" << std::endl;
}

class Naughty: public alfs::destructionEvent::Source
{
public:
    virtual ~Naughty() {}
    double pi() const { return 3.14; }
    void selfDestroy() { delete this; }
};

void testAccessCheck()
{
    using namespace alfs;
    std::cout << "\n* testAccessCheck:" << std::endl;
    bool stdExceptionExpected = false;
    try
    {
        CheckingPtr<Naughty>    p( new Naughty );

        p->pi();
        std::cout << "Good, access of valid pointer succeeded" << std::endl;
        p->selfDestroy();
        stdExceptionExpected = true;  p->pi();
        throw 666;
    }
    catch( int )
    {
        throwX( "Failed testAccessCheck" );
    }
    catch( std::exception const& )
    {
        if( !stdExceptionExpected ) { throw; }
        std::cout << "Good, access of invalid pointer failed" << std::endl;
    }
    std::cout << "Passed testAccessCheck" << std::endl;
}


class PolymorphicA { public: virtual ~PolymorphicA() {} };
class PolymorphicB { public: virtual ~PolymorphicB() {} };
class Polymorphic: public PolymorphicA, public PolymorphicB, public alfs::destructionEvent::Source {};

class PolymorphicUnrelated { public: ~PolymorphicUnrelated() {} };

void testDynamicConversion()
{
    using namespace alfs;
    std::cout << "\n* testDynamicConversion:" << std::endl;
    {
        CheckingPtr<Polymorphic>     p( new Polymorphic );
        CheckingPtr<PolymorphicA>    a = p;

        CheckingPtr<PolymorphicB>    b = a.convertedOrVoid();
        (b.hasValidReferent()) || throwX( "Failed dynamic conversion (init) to related type" );

        b = a.convertedOrVoid();
        (b.hasValidReferent()) || throwX( "Failed dynamic conversion (asgn) to related type" );

        CheckingPtr<PolymorphicUnrelated>    u = a.convertedOrVoid();
        (u.isVoid()) || throwX( "Incorrectly succeeded dynamic conversion (init) to unrelated type" );

        u = a.convertedOrVoid();
        (u.isVoid()) || throwX( "Incorrectly succeeded dynamic conversion (asgn) to unrelated type" );
    }
    std::cout << "Passed testDynamicConversion" << std::endl;
}


int main()
{
    static char const testName[] = "CheckingPtr test 01";
    using namespace alfs;
    std::cout << testName << std::endl;
    try
    {
        testAutoDestroy();
        testAccessCheck();
        testDynamicConversion();

        std::cout << "\nPassed all tests." << std::endl;
        return EXIT_SUCCESS;
    }
    catch( std::exception const& x )
    {
        std::cout << "Failed" << std::endl;
        std::cerr << "!" << x.what() << std::endl;
        return EXIT_FAILURE;
    }
}
